// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: donggu

#pragma once

#include <algorithm>
#include <iterator>
#include <map>
#include <memory>
#include <optional>
#include <vector>

/// Book Builder
///
/// - All data types are double (price, qty)
/// - Ask/Bid crossing check is applied to all exchanges

struct PriceQty {
  double price;
  double qty;
  PriceQty(double price, double qty) : price(price), qty(qty) {}
};

inline bool operator==(const PriceQty& self, const PriceQty& other) {
  return self.price == other.price && self.qty == other.qty;
}

inline bool operator!=(const PriceQty& self, const PriceQty& other) {
  return self.price != other.price || self.qty != other.qty;
}

// do not put product here

class IBook {
 public:
  using AskIterator = std::map<double, double>::const_iterator;
  using BidIterator = std::map<double, double>::const_reverse_iterator;

  virtual ~IBook() = default;
  virtual bool IsSnapshot() const = 0;
  virtual int64_t Timestamp() const = 0;

  virtual std::optional<double> AskQty(double price) const = 0;
  virtual std::optional<double> BidQty(double price) const = 0;

  virtual std::optional<PriceQty> Ask0() const = 0;
  virtual std::optional<PriceQty> Bid0() const = 0;
  virtual std::optional<PriceQty> AskN(size_t n) const = 0;
  virtual std::optional<PriceQty> BidN(size_t n) const = 0;
  virtual AskIterator AskBegin() const = 0;
  virtual AskIterator AskEnd() const = 0;
  virtual BidIterator BidBegin() const = 0;
  virtual BidIterator BidEnd() const = 0;

  virtual const std::map<double, double>& Asks() const = 0;
  virtual boost::reversed_range<const std::map<double, double>> Bids() const = 0;

  virtual size_t AskSize() const = 0;
  virtual size_t BidSize() const = 0;

  /// return ask_0..ask_n (n+1 elements)
  virtual std::vector<PriceQty> Asks(int n) const = 0;

  /// return bid_0..bid_n (n+1 elements)
  virtual std::vector<PriceQty> Bids(int n) const = 0;

  virtual const std::vector<PriceQty>& DeltaAsks() const = 0;
  virtual const std::vector<PriceQty>& DeltaBids() const = 0;
};
